package com.example.ldxstc.english_master;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.PopupWindow;
import android.widget.TextView;

/**
 * Created by will on 10/20/14.
 */
public class PracticeView extends TextView {
  public static int CURSOR_WIDTH = 4;
  private int mTouchX;
	private int mTouchY;
  private int[] currentCood = new int[2];
  private boolean selected = false;
  private Canvas canvas;
  private ImageSpan cursorLine;
  private CursorHandle cursor;

  private static String TAG = PracticeView.class.getName();

  public PracticeView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public void initiateCursor() {
    Log.d(TAG, "initiateCursor ");
    Layout layout = getLayout();
    int height = layout.getLineTop(0) - layout.getLineBottom(0);
    cursor = new CursorHandle(this, CURSOR_WIDTH, Math.abs(height));
    this.getAdjusteStartXY(0, currentCood);
    Log.i(TAG, "coords are: " + currentCood[0] + " " + currentCood[1]);
		cursor.show(currentCood[0], currentCood[1]);
  }

  /**
	 * Gets the character offset where (x, y) is pointing to.
	 *
	 * @param x x coordinate relative to this TextView
	 * @param y y coordinate relative to this TextView
	 * @return the offset at (x, y), -1 if error occurs
	 *
	 * @see {@link #getOffset(int, int)}
	 */
	public int getPreciseOffset(int x, int y) {
		Layout layout = getLayout();
    y = y + this.getScrollY();

		if (layout != null) {
			int topVisibleLine = layout.getLineForVertical(y);
			int offset = layout.getOffsetForHorizontal(topVisibleLine, x);

			int offset_x = (int) layout.getPrimaryHorizontal(offset);
			if (offset_x > x) {
				return layout.getOffsetToLeftOf(offset);
			}
		}
		return getOffset(x, y);
	}

  /**
	 * Gets the character offset of (x, y). If (x, y) lies on the right half of the character, it
	 * returns the offset of the next character. If (x, y) lies on the left half of the character, it
	 * returns the offset of this character.
	 *
	 * @param x x coordinate relative to this TextView
	 * @param y y coordinate relative to this TextView
	 * @return the offset at (x,y), -1 if error occurs
	 */
	public int getOffset(int x, int y) {
		Layout layout = getLayout();
		int offset = -1;

		if (layout != null) {
			int topVisibleLine = layout.getLineForVertical(y);
			offset = layout.getOffsetForHorizontal(topVisibleLine, x);
		}

		return offset;
	}

  /**
	 * Get the (x,y) screen coordinate from the specified offset. If the specified offset is beyond the
	 * end of the line, move the offset to the beginning of the next line.
	 *
	 * @param offset   the offset
	 * @param coords   the returned x, y coordinate array, muust have a length of 2
	 */
	private void getAdjusteStartXY(int offset, int[] coords) {
		if (offset < getText().length()) {
			final Layout layout = getLayout();
			if (layout != null) {
				if (isEndOfLineOffset(offset + 1)) {
					float a = layout.getPrimaryHorizontal(offset);
					float b = layout.getLineRight(layout.getLineForOffset(offset));
					if (a == b) {
						// this means the we encounter a new line character, i think.
						offset += 1;
					}
				}
			}
		}
		getXY(offset, coords);
	}

  /**
	 * Checks whether the specified offset is at the end of a line.
	 * <p/>
	 * PRECONDITION: assumes layout exists and is valid
	 *
	 * @param offset the offset to check
	 * @return true if the offset is at the end of a line, false otherwise.
	 */
	private boolean isEndOfLineOffset(int offset) {
		if (offset > 0) {
			return getLayout().getLineForOffset(offset) ==
					  getLayout().getLineForOffset(offset - 1) + 1;
		}
		return false;
	}

  /**
	 * get the (x,y) screen coordinates from the specified offset.
	 *
	 * @param offset   the offset
	 * @param coords   the returned x, y coordinate array, must have a length of 2
	 */
	private void getXY(int offset, int[] coords) {
		assert (coords.length >= 2);

		coords[0] = coords[1] = -1;
		Layout layout = getLayout();

		if (layout != null) {
			int line = layout.getLineForOffset(offset);
      int top = layout.getLineTop(line);

			coords[0] = (int) layout.getPrimaryHorizontal(offset); // x
			coords[1] = top - this.getScrollY(); // y
		}
	}

  public class CursorHandle extends View {

    private PracticeView practiceView;
    private PopupWindow container;
    private Drawable cursor;
    private int height;
    private int width;
    private boolean showing;

    public CursorHandle(PracticeView practiceView, int width, int height) {
      super(practiceView.getContext());
      this.practiceView = practiceView;
      cursor = getResources().getDrawable(R.drawable.ic_cursor);
      container = new PopupWindow(this);
      container.setClippingEnabled(false);
      container.setHeight(height);
      container.setWidth(width);
      this.width = width;
      this.height = height;
      showing = false;
      invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
      Log.i(TAG, "Dimension: " + width + " : " + height);
      cursor.setBounds(0, 0, width, height);
      cursor.draw(canvas);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
      Log.i(TAG, "CursorHandle onTouch " + event.getAction());
      switch (event.getAction()) {
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE: {
          mTouchX = (int) event.getX();
          mTouchY = (int) event.getY();
          Log.i(TAG, "onTouch x: " + mTouchX + ", Y: " + mTouchY);
          showCursor(mTouchX, mTouchY);
        }
        case MotionEvent.ACTION_CANCEL:
        default:
          break;
      }
      return true;
    }

    public boolean isShowing() {
      return container.isShowing();
    }

    /**
     * Show the cursor pointing to the specified point.
     *
     * @param x the x coordinate of the point relative to the TextView
     * @param y the y coordinate of the point relative to the TextView
     */
    public void show(int x, int y) {
      final int[] coords = new int[2];

      PracticeView.this.getLocationInWindow(coords);
      coords[0] += x;
      coords[1] += y;
      if (!showing) {
        container.showAtLocation(PracticeView.this, Gravity.NO_GRAVITY, coords[0], coords[1]);
      } else {
        pointTo(coords[0], coords[1]);
      }
      showing = true;
    }


    /**
     *
     *
     * @param mTouchX
     * @param mTouchY
     */
    public void showCursor(int mTouchX, int mTouchY) {
      int offset = practiceView.getPreciseOffset(mTouchX, mTouchY);
      Log.i(TAG, "showCursor offset " + offset);
      practiceView.getAdjusteStartXY(offset, currentCood);
      Log.i(TAG, "coords are: " + currentCood[0] + " " + currentCood[1]);
      show(currentCood[0], currentCood[1]);
    }

    /**
     * move the cursor to point the (x,y) location on the screen.
     *
     * @param x the x coordinate on the screen
     * @param y the y coordinate on the screen
     */
    private void pointTo(int x, int y) {
      if (isShowing()) {
        container.update(x, y, -1, -1);
      }
    }
  }
}


